#include <tuple>
#include <string>
#include <iostream>
#include <functional>
#include <type_traits>
#include <utility>

template <typename Function, typename ...CapturedArgs>
class curried {
    public:
        curried(Function function, CapturedArgs... args)
            : m_function(function)
            , m_captured(std::make_tuple(args...))
        {

        }
        
        curried(Function function, std::tuple<CapturedArgs...> args)
            : m_function(function)
            , m_captured(args)
        {

        }
        //std::decay_t 是 C++14 中引入的一个类型转换工具，它基于 std::decay，
        //用于将某种类型转换为“衰变”后的类型。衰变的过程涉及以下几种转换：
        //如果是数组类型，衰变为指针类型。
        //如果是函数类型，衰变为指针类型。
        //如果是引用类型，去除引用。
        //去除 const 和 volatile 限定符。

        template <typename ...NewArgs>
        auto operator()(NewArgs&& ...args) const
        {
            auto new_args = std::make_tuple(std::forward<NewArgs>(args)...);
            auto all_args = std::tuple_cat(m_captured, std::move(new_args));

            if constexpr(std::is_invocable_v<Function, CapturedArgs..., NewArgs...>) {
                return std::apply(m_function, all_args);
            } else {
                return curried<Function, CapturedArgs..., NewArgs...>(
                    m_function, all_args);
            }
        }

        private:
            Function m_function;
            std::tuple<CapturedArgs...> m_captured;
};

// Needed for pre-C++17 compilers
template <typename Function>
curried<Function> make_curried(Function &&f)
{
    return curried<Function>(std::forward<Function>(f));
}

class callable_test {
    public:
    template <typename T1, typename T2, typename T3, typename T4>
        auto operator() (T1 x, T2 y, T3 z, T4 b) const
        {
            std::cout << x << "," << y << "," << z << "." << b << std::endl;
            return x + y + z;
        }
        template <typename T1, typename T2, typename T3>
        auto operator() (T1 x, T2 y, T3 z) const
        {
            std::cout << x << "," << y << "," << z << "." << std::endl;
            return x + y + z;
        }

        template <typename T1, typename T2>
        auto operator() (T1 x, T2 y) const
        {
            std::cout << x << "," << y << ","  << std::endl;
            return x + y;
        }
};

int main(int argc, char *argv[])
{
    auto less_curried = curried(std::less<>());

    std::cout << less_curried(42, 1) << std::endl;

    auto greater_than_42 = less_curried(42);

    std::cout << greater_than_42(1.0) << std::endl;
    std::cout << greater_than_42(100.0) << std::endl;

    callable_test ct;

    auto ct_curried = curried(ct);

    std::cout <<ct_curried(1)(2) << std::endl;
    std::cout <<ct_curried(1)(2, 8, 10) << std::endl;
    auto ct_curried_one = curried(ct, 1);

    std::cout << ct_curried_one(2,3) << std::endl;

    return 0;
}